package com.five.years.common.utils;


import com.five.years.common.Constants;
import com.five.years.common.SystemCode;
import com.five.years.common.exception.BusinessException;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;

import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class RedisUtils {

    private static RedisTemplate redisTemplate = SpringUtils.getBean("redisTemplate");

    public static final RedisInstance REDIS = SpringUtils.getBean("redisInstance");

    public static final RedisInstance REDIS_SET = SpringUtils.getBean("redisInstanceSet");

    public static final RedisInstance REDIS_FOCUS = SpringUtils.getBean("redisInstanceFocus");

    public static final RedisInstance REDIS_TOKEN = SpringUtils.getBean("redisInstanceToken");

    public static final RedisInstance REDIS_BLOCKCHAIN = SpringUtils.getBean("redisInstanceBlockChain");

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);

    private static final Map<String, String> cacheL2Map = new ConcurrentHashMap<>();


    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    @SuppressWarnings("unchecked")
    public static void removePattern(final String pattern) {
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void remove(final String key) {
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    public static long getExpire(final String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static String getStr(final String key) {
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        return String.valueOf(operations.get(key));
    }

    public static Long getLong(final String key) {
        return REDIS.getLong(key);
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean set(final String key, Object value, Long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public static Long increment(final String key, long value) {
        try {
            ValueOperations<Serializable, Long> operations = redisTemplate.opsForValue();
            return operations.increment(key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 设置有效期
     *
     * @param key
     * @param expireTime
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean expire(final String key, int expireTime) {
        try {
            return redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static Jedis getResource() throws JedisException {
        return REDIS.getPool().getResource();
    }

    public static Set<String> getSet(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    public static RedisLocker acquireLocker(String key, int waitSecond, int expireSecond) {
        return acquireLocker(key, waitSecond, expireSecond, true);
    }

    public static RedisLocker acquireLocker(String key, int expireSecond) {
        return acquireLocker(key, 1, expireSecond, false);
    }

    public static RedisLocker acquireLocker(String key, int waitSecond, int expireSecond, boolean failThrowE) {
        if (expireSecond < 1) {
            expireSecond = 6;
        }
        if (waitSecond < 0) {
            waitSecond = 0;
        }
        RLock rLock = REDIS.getRedissonClient().getLock(Constants.RedisLocker.LOCKER_PREFIX + key);
        try {
            if (rLock.tryLock(waitSecond, expireSecond, TimeUnit.SECONDS)) {
                return new RedisLocker(rLock);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (failThrowE) {
            throw new BusinessException(SystemCode.OPTIMISTIC_LOCK_RETRY);
        } else {
            return null;
        }
    }

    /**
     * 释放资源
     *
     * @param jedis
     */
    public static void returnResource(Jedis jedis) {
        if (jedis != null) {
            try {
                REDIS.getPool().returnResource(jedis);
            } catch (Exception e) {
                REDIS.getPool().returnBrokenResource(jedis);
            }
        }
    }

    public static class RedisLocker {
        private RLock rLocker;

        public RedisLocker(RLock rLocker) {
            this.rLocker = rLocker;
        }

        public void release() {
            if (rLocker != null) {
                rLocker.unlock();
            }
        }
    }

    public static boolean isTrue(String key) {
        return Constants.TRUE.equals(REDIS.get(key));
    }

    public static Long rPush(String key, String... values) {
        return REDIS.rPush(key, values);
    }

    public static boolean isL2True(String key) {
        return Constants.TRUE.equals(cacheL2Val(key));
    }

    public static void clearCacheL2() {
        cacheL2Map.clear();
    }

    public static String cacheL2Val(String key) {
        if (cacheL2Map.containsKey(key)) {
            return cacheL2Map.get(key);
        } else {
            String val = REDIS.get(key);
            cacheL2Map.put(key, val);
            return val;
        }
    }

    /**
     * @Author jiangfeng.zhu
     * @Description 在当前线程内释放锁资源
     * @Date 15:44 2020/8/7
     **/
    public static void unLocker(String key) {
        try {
            RLock rLock = REDIS.getRedissonClient().getLock(Constants.RedisLocker.LOCKER_PREFIX + key);

            //释放出错，程序继续执行
            rLock.unlock();
        }catch (Exception e){

        }
    }
}
